#include "PhysXLoader.h"
#include "NxStream.h"
#include "NxPhysics.h"
#include "NxCooking.h"
#include "BSPCompile.h"
#include "MathExtend.h"
#include "Windows/GxWinApp.h"
#include "Basic/FileSystem.h"
#include "PhysXWrappers.h"
#include "PhysicsScene.h"

using namespace GxLibBasic;
using namespace GxWinGUI;
using namespace FinalRendering::Engine;
using namespace FinalRendering::Engine::Physics;

namespace FinalRendering
{
	const float PvsPortalGridSize = 32.0f;

	class iVertex
	{
	public:
		vec3 pos, normal;
		vec3 texCoord, lightmapCoord;
	};

	class iFace
	{
	public:
		bool isDetail, isDivider;
		int matID, lightmapID;
		//float width, height; // bounding rectangle size
		//float lightmapScale;
		Array<iVertex> vertices;
		iFace()
		{
			isDetail = isDivider = false;
			matID = -1;
		}
		void AssignProperty(iFace & f)
		{
			isDetail = f.isDetail;
			isDivider = f.isDivider;
			matID = f.matID;
			lightmapID = f.lightmapID;
		}
	};

	class iBspNode
	{
	public:
		Plane plane;
		RefPtr<iBspNode> children[2];
		Array<RefPtr<iFace>> faces;
	};

	class iScene
	{
	public:
		vec3 BBoxMin, BBoxMax;
		Array<LightmapInfo, true> Lightmaps;
		Array<String> matNames;
		RefPtr<iBspNode> bspRoot; 
	};

	void EvalPolygonRect(ConvexHull::Polygon & p, vec3 & axisX, vec3 & axisY, float & xmin, float & xmax, 
		float & ymin, float & ymax)
	{
		axisX = p.vertices[1]-p.vertices[0];
		Normalize(axisX, axisX);
		vec3 norm(p.plane.a,p.plane.b,p.plane.c);
		axisY = VectorCross(axisX, norm);
		Normalize(axisY, axisY);
		xmin = ymin = 1e9;
		xmax = ymax = -1e9;
		for (int i=1; i<p.vertices.Count(); i++)
		{
			vec3 v = (p.vertices[i]-p.vertices[0]);
			float x = axisX * v;
			float y = axisY * v;
			if (x<xmin)
				xmin = x;
			if (x>xmax)
				xmax = x;
			if (y<ymin)
				ymin = y;
			if (y>ymax)
				ymax = y;
		}
	}

	int EvalCost(Array<RefPtr<iFace>> & faces, int id)
	{
		int pos=0, neg=0, inter=0;
		for (int i=0; i<faces.Count(); i++)
		{
			if (i == id)
				continue;
			int vl=0, vr=0;
			Plane plane;
			PlaneFromVertices(plane, faces[i]->vertices[0].pos,
				faces[i]->vertices[1].pos,
				faces[i]->vertices[2].pos);
			for (int j=0; j<faces[i]->vertices.Count(); j++)
			{
				vec3 p = faces[i]->vertices[j].pos;
				float val = plane.a*p.x+ plane.b*p.y + plane.c *p.z + plane.d;
				if (val >= 0.0f)
					vl++;
				else
					vr++;
			}
			if (vl!=0 && vr!=0)
				inter++;
			else if (vl!=0)
				pos++;
			else
				neg++;
		}
		return abs(pos-neg)+4*inter;
	}

	int ChooseSplittingPlane(Array<RefPtr<iFace>> & faces)
	{
		int minCost = 0x7FFFFFFF;
		int result = -1;
		for (int i=0; i<faces.Count(); i++)
		{
			if (!faces[i]->isDetail && !faces[i]->isDivider)
			{
				int cost = EvalCost(faces, i);
				if (cost < minCost)
				{
					minCost = cost;
					result = i;
				}
			}
		}
		return result;
	}

	void BSP_Compiler::BSP_Partition(iBspNode * node)
	{
		int id = ChooseSplittingPlane(node->faces);
		if (id != -1)
		{
			Plane plane;
			iFace * face = node->faces[id].operator ->();
			PlaneFromVertices(plane, face->vertices[0].pos,
				face->vertices[1].pos, face->vertices[2].pos);
			node->children[0] = new iBspNode();
			node->children[1] = new iBspNode();
			node->faces[id]->isDivider = true;
			node->plane = plane;
			for (int i=0; i<node->faces.Count(); i++)
			{
				Array<vec3> vIn, vPos, vNeg;
				Editor::Polygon p, pNeg, pPos;
				int vposNum=0, vnegNum=0;
				for (int j=0; j<node->faces[i]->vertices.Count(); j++)
				{
					vec3 pos = node->faces[i]->vertices[j].pos;
					float side = plane.PointDistance(pos);
					if (side > Epsilon)
						vposNum++;
					else if (side < -Epsilon)
						vnegNum++;
					vIn.Add(node->faces[i]->vertices[j].pos);
					Editor::Vertex v;
					v.normal = node->faces[i]->vertices[j].normal;
					v.pos = j;
					v.TexCoord[0] = node->faces[i]->vertices[j].texCoord;
					v.TexCoord[1] = node->faces[i]->vertices[j].lightmapCoord;
					p.vertices.Add(v);
				}
				if (vposNum!=0 && vnegNum != 0)
				{
					Editor::Edge eNeg,ePos;
					Editor::Polygon::SplitPolygon(plane, vIn, p, vNeg, pNeg, vPos, pPos, eNeg, ePos);
					if (vPos.Count() > 2)
					{
						iFace * nface = new iFace();
						nface->AssignProperty(*(node->faces[i]));
						for (int j=0; j<vPos.Count(); j++)
						{
							iVertex v;
							v.pos = vPos[j];
							v.normal = pPos.vertices[j].normal;
							v.texCoord = pPos.vertices[j].TexCoord[0];
							v.lightmapCoord = pPos.vertices[j].TexCoord[1];
							nface->vertices.Add(v);
						}
						node->children[0]->faces.Add(nface);
					}
					if (vNeg.Count() > 2)
					{
						iFace * nface = new iFace();
						nface->AssignProperty(*(node->faces[i]));
						for (int j=0; j<vNeg.Count(); j++)
						{
							iVertex v;
							v.pos = vNeg[j];
							v.normal = pNeg.vertices[j].normal;
							v.texCoord = pNeg.vertices[j].TexCoord[0];
							v.lightmapCoord = pNeg.vertices[j].TexCoord[1];
							nface->vertices.Add(v);
						}
						node->children[1]->faces.Add(nface);
					}
				}
				else if (vposNum != 0)
				{
					node->children[0]->faces.Add(node->faces[i]);
				}
				else if (vnegNum != 0)
				{
					node->children[1]->faces.Add(node->faces[i]);
				}
				else
				{
					vec3 n = node->faces[i]->vertices[0].normal;
					float side = n * vec3(plane.a,plane.b,plane.c);
					if (side>0.0f)
						node->children[0]->faces.Add(node->faces[i]);
					else
						node->children[1]->faces.Add(node->faces[i]);
				}
			}
			BSP_Partition(node->children[0].operator ->());
			OnCheckCancel.Invoke(cancelled);
			if (cancelled)
				return;
			BSP_Partition(node->children[1].operator ->());
			OnCheckCancel.Invoke(cancelled);
			if (cancelled)
				return;
		}
	}

	bool TestConnectivity(iFace * f1, iFace * f2)
	{
		for (int i=0; i<f1->vertices.Count(); i++)
			for (int j=0; j<f2->vertices.Count(); j++)
			{
				float dist = (f1->vertices[i].pos - f2->vertices[j].pos).GetLength2();
				if (dist < 0.1f)
					return true;
			}
		return false;
	}

	void GenerateLightmap(iScene * iscene, Array<RefPtr<iFace>> &faces, Array<Plane> & planes,
		Array<float> & lmScales)
	{
		Array<bool, true> packed;
		Array<int> polySet;
		packed.SetSize(faces.Count());
		for (int i=0; i<packed.Count(); i++)
			packed[i] = false;
		for (int i=0; i<faces.Count(); i++)
		{
			if (packed[i])
				continue;
			packed[i] = true;
			polySet.Clear();
			polySet.Add(i);
			// Find coplanar and connected polygon set
			bool added = true;
			while (added)
			{
				added = false;
				for (int k=0; k<polySet.Count(); k++)
				{
					for (int j=i+1; j<faces.Count(); j++)
					{
						if (packed[j])
							continue;
						int tid = polySet[k];
						if (planes[j] == planes[tid] && 
							lmScales[j] == lmScales[tid]
							&& TestConnectivity(faces[j].operator ->(), faces[tid].operator ->()))
						{
							polySet.Add(j);
							packed[j] = true;
							added = true;
						}
					}
				}
			}
			// Create Rectangular Axis
			iFace & face0 = *faces[polySet[0]];
			vec3 normal;
			memcpy(&normal, &planes[polySet[0]], sizeof(float)*3);
			Normalize(normal, normal);
			LightmapInfo lm;
			lm.AxisX = face0.vertices[1].pos - face0.vertices[0].pos;
			Normalize(lm.AxisX, lm.AxisX);
			lm.AxisY = VectorCross(lm.AxisX, normal);
			Normalize(lm.AxisY, lm.AxisY);
			lm.OriPos = face0.vertices[0].pos;
			lm.Normal = normal;
			// Get coordinates
			float minX=1e9f, minY=1e9f;
			float maxX=-1e9f, maxY=-1e9f;
			for (int j=0; j<polySet.Count(); j++)
			{
				int pid = polySet[j];
				for (int k=0; k<faces[pid]->vertices.Count(); k++)
				{
					vec3 d = (faces[pid]->vertices[k].pos-lm.OriPos);
					float x = d*lm.AxisX;
					float y = d*lm.AxisY;
					if (x<minX) minX = x;
					if (x>maxX) maxX = x;
					if (y<minY) minY = y;
					if (y>maxY) maxY = y;
					faces[pid]->vertices[k].lightmapCoord.x = x;
					faces[pid]->vertices[k].lightmapCoord.y = y;
				}
			}
			float w = maxX - minX;
			float h = maxY - minY;

			lm.OriPos += lm.AxisX * minX;
			lm.OriPos += lm.AxisY * minY;
			lm.AxisX *= w;
			lm.AxisY *= h;
			lm.pixHeight = (int)ceil(h/lmScales[i]);
			lm.pixWidth = (int)ceil(w/lmScales[i]);
			if (lm.pixWidth < 2) lm.pixWidth = 2;
			if (lm.pixWidth > 1000) lm.pixWidth = 1000;
			if (lm.pixHeight < 2) lm.pixHeight = 2;
			if (lm.pixHeight > 1000) lm.pixHeight = 1000;
			for (int j=0; j<polySet.Count(); j++)
			{
				int pid = polySet[j];
				for (int k=0; k<faces[pid]->vertices.Count(); k++)
				{
					faces[pid]->vertices[k].lightmapCoord.x = 
						(faces[pid]->vertices[k].lightmapCoord.x-minX)/w;
					faces[pid]->vertices[k].lightmapCoord.y = 
						(faces[pid]->vertices[k].lightmapCoord.y-minY)/h;
				}
				faces[pid]->lightmapID = iscene->Lightmaps.Count();
			}
			iscene->Lightmaps.Add(lm);
		}
	}

	void BSP_Compiler::CompileIntermediateScene(iScene * iscene)
	{
		iscene->BBoxMax = vec3(-1e9f, -1e9f, -1e9f);
		iscene->BBoxMin = vec3(1e9f, 1e9f, 1e9f);
		for (int i=0; i<brushes.Count(); i++)
			for (int j=0; j<brushes[i]->vertices.Count(); j++)
			{
				vec3 &vert = brushes[i]->vertices[j];
				if (vert.x > iscene->BBoxMax.x) iscene->BBoxMax.x = vert.x;
				if (vert.y > iscene->BBoxMax.y) iscene->BBoxMax.y = vert.y;
				if (vert.z > iscene->BBoxMax.z) iscene->BBoxMax.z = vert.z;
				if (vert.x < iscene->BBoxMin.x) iscene->BBoxMin.x = vert.x;
				if (vert.y < iscene->BBoxMin.y) iscene->BBoxMin.y = vert.y;
				if (vert.z < iscene->BBoxMin.z) iscene->BBoxMin.z = vert.z;
			}
		iscene->bspRoot = new iBspNode();
		int polyCount = 0;
		for (int i=0; i<brushes.Count(); i++)
			polyCount += brushes[i]->Polygons.Count();
		Array<RefPtr<iFace>> & poly = iscene->bspRoot->faces;
		Array<Plane> planes;
		Array<float> lmScales;
		lmScales.SetCapacity(polyCount);
		planes.SetCapacity(polyCount);
		poly.SetCapacity(polyCount);
		for (int i=0; i<brushes.Count(); i++)
		{
			for (int j=0; j<brushes[i]->Polygons.Count(); j++)
			{
				if (brushes[i]->Polygons[j].Ignore)
					continue;
				RefPtr<iFace> nface = new iFace();
				nface->isDetail = (brushes[i]->Style == Editor::Brush::bsDetail);
				String matName = brushes[i]->Polygons[j].MatName;
				nface->matID = iscene->matNames.CombineItem(matName);
				Plane plane;
				memcpy(&plane, &brushes[i]->Polygons[j].Normal, sizeof(float)*3);
				plane.d = -brushes[i]->vertices[brushes[i]->Polygons[j].vertices[0].pos]*
					brushes[i]->Polygons[j].Normal;
				planes.Add(plane);
				lmScales.Add(brushes[i]->Polygons[j].TexcoordSystem.LightmapScale);

				for (int k=0; k<brushes[i]->Polygons[j].vertices.Count(); k++)
				{
					iVertex vert;
					vert.pos = brushes[i]->vertices[brushes[i]->Polygons[j].vertices[k].pos];
					vert.normal = brushes[i]->Polygons[j].Normal;
					vert.texCoord = brushes[i]->Polygons[j].vertices[k].TexCoord[0];
					nface->vertices.Add(vert);
				}
				if (nface->vertices.Count()>=3)
					poly.Add(nface);
			}
		}
		GenerateLightmap(iscene, poly, planes, lmScales);
		BSP_Partition(iscene->bspRoot.operator ->());
	}

	float ClockwiseDot(vec3 &v1, vec3 &v2, vec3 &normal)
	{
		float dot = v1*v2;
		vec3 v;
		v = VectorCross(v1,v2);
		Normalize(v,v);
		float f = v * normal;
		if (f<0.0f)
			dot = -dot -2;
		return dot;
	}

	void SortPolygonVertices(vec3 normal, Array<vec3> & verts)
	{
		vec3 v;
		for (int j=0;j<verts.Count();j++)
			v += verts[j];
		float div = 1.0f/verts.Count();
		v.x *= div;  v.y *= div;  v.z *= div;
		vec3 v0 = verts[0] - v;
		Normalize(v0,v0);
		vec3 v1;
		// Selection Sort
		for (int j=0; j<verts.Count()-2;j++)
		{
			float maxdot = -4.0f;
			int next = -1;
			for (int k=j+1; k<verts.Count();k++)
			{
				v1 = verts[k] - v;
				Normalize(v1,v1);		
				float curdot = ClockwiseDot(v0,v1,normal);
				if (curdot>maxdot)
				{
					maxdot = curdot;
					next = k;
				}
			}
			if (next == j+1)
				continue;
			vec3 tmpv = verts[j+1];
			verts[j+1] = verts[next];
			verts[next] = tmpv;
		}
	}

	void ComputeConvexHull(FinalRendering::Engine::Scene * scene, int nodeID)
	{
		BSP_Node * node = &scene->BSP[nodeID];
		ConvexHull * hull = &node->LeafInfo->BoundingHull;
		Array<Plane> planes;
		planes.Add(Plane(1,0,0,-scene->BBoxMin.x));
		planes.Add(Plane(0,1,0,-scene->BBoxMin.y));
		planes.Add(Plane(0,0,1,-scene->BBoxMin.z));
		planes.Add(Plane(-1,0,0,scene->BBoxMax.x));
		planes.Add(Plane(0,-1,0,scene->BBoxMax.y));
		planes.Add(Plane(0,0,-1,scene->BBoxMax.z));
		for (int i=0; i<planes.Count(); i++)
		{
			ConvexHull::Polygon * poly = new ConvexHull::Polygon();
			poly->plane = planes[i];
			hull->Faces.Add(poly);
		}
		// Step 1, Collect divide planes of this node
		{
			BSP_Node * tnode = node;
			while (tnode->HasParent())
			{
				BSP_Node * lstNode = tnode;
				tnode = &scene->BSP[tnode->Parent];
				Plane p;
				memcpy(&p, tnode->DividePlane, sizeof(float)*4);
				if (tnode->Children[1]!=0xFFFF && (&scene->BSP[tnode->Children[1]] == lstNode))
				{
					p.a = -p.a;
					p.b = -p.b;
					p.c = -p.c;
					p.d = -p.d;
				}
				
				bool added = planes.Combine(p);
				if (added)
				{
					ConvexHull::Polygon * poly = new ConvexHull::Polygon();
					poly->plane = p;
					hull->Faces.Add(poly);
				}
			}
		}
		Array<vec3> verts;
		// Step 2, Calculate all intersection points of planes
		{
			hull->Faces.SetSize(planes.Count());
			#define MAT_DET(mat) (mat[0][0]*mat[1][1]*mat[2][2] + \
					mat[0][1]*mat[1][2]*mat[2][0]+mat[0][2]*mat[1][0]*mat[2][1] \
					-mat[0][0]*mat[1][2]*mat[2][1]- mat[0][1]*mat[1][0]*mat[2][2] \
					-mat[0][2]*mat[1][1]*mat[2][0]);
			// mat is the matrix built from the normals of the three Planes.
			float mat[3][3],m[3][3];
			float vf[3];
			vec3 v;
			float f1,f2;
			for (int i=0;i<planes.Count()-2;i++)
				for (int j=i+1;j<planes.Count()-1;j++)
					for (int k=j+1;k<planes.Count();k++)
					{
						mat[0][0] = planes[i].a; mat[0][1] = planes[i].b; mat[0][2] = planes[i].c;
 						mat[1][0] = planes[j].a; mat[1][1] = planes[j].b; mat[1][2] = planes[j].c;
						mat[2][0] = planes[k].a; mat[2][1] = planes[k].b; mat[2][2] = planes[k].c;
						f1 = MAT_DET(mat);
						if (f1>=-Epsilon && f1<=Epsilon)
							continue;
						for (int l=0;l<3;l++)
						{
							m[0][0] = mat[0][0];  m[0][1] = mat[0][1];  m[0][2] = mat[0][2];
							m[1][0] = mat[1][0];  m[1][1] = mat[1][1];  m[1][2] = mat[1][2];
							m[2][0] = mat[2][0];  m[2][1] = mat[2][1];  m[2][2] = mat[2][2];
							m[0][l] = -planes[i].d;
							m[1][l] = -planes[j].d;
							m[2][l] = -planes[k].d;
							f2 = MAT_DET(m);
							vf[l] = f2/f1;
						}
						v.x = vf[0]; v.y = vf[1]; v.z = vf[2];
						int l;
						for (l=0; l<planes.Count();l++)
							if (planes[l].PointDistance(v)<-Epsilon)
								break;
						if (l<planes.Count())
							continue;
						verts.CombineItem(v);
					}
		}
		// Step 3, Cull out invalid points
		{
			for (int i=0;i<verts.Count();i++)
			{
				bool removed = false;
				for (int j=0; j<hull->Faces.Count(); j++)
				{
					if (hull->Faces[j]->plane.PointDistance(verts[i])<-0.1f)
					{
						verts.Delete(i);
						i--;
						removed = true;
						break;
					}
				}
				if (!removed)
				{
					for (int j=0;j<hull->Faces.Count();j++)
					{
						if (fabs(hull->Faces[j]->plane.PointDistance(verts[i]))<=0.1f)
						{
							hull->Faces[j]->vertices.CombineItem(verts[i]);
						}
					}
				}
			}
			int id=0;
			while (id<hull->Faces.Count())
			{
				if (hull->Faces[id]->vertices.Count()<3)
				{
					hull->Faces[id] = 0;
					hull->Faces.Delete(id);
				}
				else
					id ++;
			}
		}
		// Step 4, Sort vertices
		{
			for (int i=0;i<hull->Faces.Count();i++)
			{
				vec3 normal(hull->Faces[i]->plane.a, hull->Faces[i]->plane.b,hull->Faces[i]->plane.c);
				Normalize(normal,normal);
				SortPolygonVertices(normal, hull->Faces[i]->vertices);
			}
		}
		// Step 5, Remove narrow hull
		{
			vec3 bmin(1e9f,1e9f,1e9f),bmax(-1e9f,-1e9f,-1e9f);
			for (int i=0; i<hull->Faces.Count(); i++)
				for (int j=0; j<hull->Faces[i]->vertices.Count(); j++)
				{
					vec3 & v = hull->Faces[i]->vertices[j];
					for (int k=0; k<3; k++)
					{
						if (v[k]>bmax[k]) bmax[k] = v[k];
						if (v[k]<bmin[k]) bmin[k] = v[k];
					}
				}
			bool narrow = false;
			for (int i=0; i<3; i++)
				if (fabs(bmax[i]-bmin[i]) < 0.1f)
				{
					narrow = true;
					break;
				}
			if (narrow)
				hull->Faces.Clear();
		
		}
	}

	void CompileFinalBSP(FinalRendering::Engine::Scene * scene, int nodeID, iBspNode * interNode)
	{
		BSP_Node * node = &scene->BSP[nodeID];
		if (interNode->children[0] || interNode->children[1])
		{
			memcpy(&node->DividePlane, &interNode->plane, sizeof(float)*4);
			if (interNode->children[0])
			{
				BSP_Node nnode;
				nnode.Parent = nodeID;
				scene->BSP.Add(nnode);
				scene->BSP[nodeID].Children[0] = scene->BSP.Count()-1;
				CompileFinalBSP(scene, scene->BSP.Count()-1, interNode->children[0].operator ->());
			}
			if (interNode->children[1])
			{
				BSP_Node nnode;
				nnode.Parent = nodeID;
				scene->BSP.Add(nnode);
				scene->BSP[nodeID].Children[1] = scene->BSP.Count()-1;
				CompileFinalBSP(scene, scene->BSP.Count()-1, interNode->children[1].operator ->());
			}
		}
		else
		{	
			node->LeafInfo = new LeafInfo();
			node->SurfaceCount = interNode->faces.Count();
			node->Surfaces = new Surface[node->SurfaceCount];
			node->PVS_ID = -1;
			for (int i=0; i<interNode->faces.Count(); i++)
			{
				node->Surfaces[i].MatID = interNode->faces[i]->matID;
				node->Surfaces[i].VertexCount = interNode->faces[i]->vertices.Count();
				node->Surfaces[i].LightmapID = interNode->faces[i]->lightmapID;
				node->Surfaces[i].Vertices = new UInt[interNode->faces[i]->vertices.Count()];
				for (int j=0; j<interNode->faces[i]->vertices.Count(); j++)
				{
					FinalRendering::Engine::Vertex v;
					v.LightmapCoord = vec2(
						interNode->faces[i]->vertices[j].lightmapCoord.x,
						interNode->faces[i]->vertices[j].lightmapCoord.y);
					v.TexCoord = vec2(
						interNode->faces[i]->vertices[j].texCoord.x,
						interNode->faces[i]->vertices[j].texCoord.y);
					v.Normal = interNode->faces[i]->vertices[j].normal;
					v.Position = interNode->faces[i]->vertices[j].pos;
					scene->Vertices.Add(v);
					node->Surfaces[i].Vertices[j] = scene->Vertices.Count()-1;
				}
			}
			ComputeConvexHull(scene, nodeID);
		}

	}

	void CompileFinalScene(iScene * iscene, FinalRendering::Engine::Scene * scene)
	{
		scene->BBoxMax = iscene->BBoxMax;
		scene->BBoxMin = iscene->BBoxMin;
		scene->MatNames.Add(iscene->matNames);
		scene->Lightmaps.Add(iscene->Lightmaps);
		BSP_Node node;
		node.Parent = -1;
		scene->BSP.Add(node);
		CompileFinalBSP(scene, 0, iscene->bspRoot.operator->());
	}

	void CompileEntity(FinalRendering::Editor::Scene * src, FinalRendering::Engine::Scene * scene)
	{
		for (int i=0; i<src->Entities.Count(); i++)
		{
			IEntity * ent = src->Entities[i]->GetEntityInterface();
			if (String(ent->GetDesc()->ClassName()) == L"EnvMapSample")
			{
				Volume vol;
				IBspEntity * bspEnt = (IBspEntity*)(ent);
				bspEnt->GetVolume(&vol);
				CubemapProbe probe;
				probe.Position = vol.Position;
				probe.Cubemap = 0;
				scene->Cubemaps.Add(probe);
			}
			else
				scene->Entities.Add(ent);
		}
		scene->ClassifyEntities();
	}

	void ExtractEdgePlanes(const ConvexHull::Polygon & p, Array<Plane> & planes)
	{
		vec3 vs;
		for (int i=0; i<p.vertices.Count(); i++)
		{
			int vi1 = i;
			int vi2 = (i+1)%p.vertices.Count();
			vec3 v1 = p.vertices[vi1];
			vec3 v2 = p.vertices[vi2];
			vs += v1;
			vec3 normal(p.plane.a,p.plane.b,p.plane.c);
			vec3 dir = v2-v1;
			vec3 pn = VectorCross(normal, dir);
			Normalize(pn,pn);
			Plane pl;
			pl.a = pn.x; pl.b = pn.y; pl.c = pn.z;
			pl.d = -pn*v1;
			planes.Add(pl);
		}
		vs *= 1.0f/p.vertices.Count();
	}

	void GetIntersectionVertices(const ConvexHull::Polygon & visitor, Array<Plane> & planes, Array<vec3> &verts)
	{
		for (int i=0; i<visitor.vertices.Count(); i++)
		{
			bool inside = true;
			vec3 & v = visitor.vertices[i];
			for (int j=0; j<planes.Count(); j++)
			{
				if (planes[j].PointDistance(v)<-0.1f)
				{
					inside = false;
					break;
				}
			}
			if (inside)
			{
				verts.CombineItem(v);
			}
		}
	}

	void GetEdgeIntersections(const ConvexHull::Polygon & p1, const ConvexHull::Polygon & p2, 
		Array<Plane> & edgePlanes1, Array<Plane> & edgePlanes2,
		Array<vec3> & verts)
	{
		vec3 normal(p1.plane.a,p1.plane.b,p1.plane.c);
		for (int i=0; i<p1.vertices.Count(); i++)
		{
			int a1,a2;
			a1 = i;
			a2 = (i+1)%p1.vertices.Count();
			vec3 & u1 = p1.vertices[a1];
			vec3 & u2 = p1.vertices[a2];
			for (int j=0; j<p2.vertices.Count(); j++)
			{
				int b1,b2;
				b1 = j;
				b2 = (j+1)%p2.vertices.Count();
				vec3 & v1 = p2.vertices[b1];
				vec3 & v2 = p2.vertices[b2];
				float dis1 = edgePlanes1[i].PointDistance(v1);
				float dis2 = edgePlanes1[i].PointDistance(v2);
				if (dis1*dis2>-0.1)
					continue;
				dis1 = edgePlanes2[j].PointDistance(u1);
				dis2 = edgePlanes2[j].PointDistance(u2);
				if (dis1*dis2>-0.1)
					continue;

				vec3 d1,d2;
				d1 = u2-u1; Normalize(d1,d1);
				d2 = v2-v1; Normalize(d2,d2);

				int a,b;
				if (fabs(normal.x) > fabs(normal.y))
					a = 0;
				else
					a = 1;
				if (fabs(normal[a])< fabs(normal.z))
					a = 2;
				if (a==0)
				{
					a = 1; b = 2;
				}
				else if (a==1)
				{
					a = 0; b = 2;
				}
				else
				{
					a = 0; b = 1;
				}

				float dist = (d2[a]*(u1[b]-v1[b])-d2[b]*(u1[a]-v1[a]))/(d2[b]*d1[a]-d2[a]*d1[b]);
				vec3 inter = u1 + d1*dist;
				verts.CombineItem(inter);
			}
		}
	}

	bool IsPolygonCoplanar(const ConvexHull::Polygon & p1, const ConvexHull::Polygon & p2)
	{
		vec3 n1(p1.plane.a,p1.plane.b,p1.plane.c), n2(p2.plane.a,p2.plane.b,p2.plane.c);
		Normalize(n1,n1); Normalize(n2,n2);
		float dot = n1*n2;
		int sgn = dot>0.0?1:-1;
		return (fabs(dot) > 0.999f && fabs(p1.plane.d-sgn*p2.plane.d)<0.1f);
	}

	void PolygonIntersection(const ConvexHull::Polygon & p1,
		const ConvexHull::Polygon & p2,
		ConvexHull::Polygon & result)
	{
		vec3 n1(p1.plane.a,p1.plane.b,p1.plane.c), n2(p2.plane.a,p2.plane.b,p2.plane.c);
		bool coplanar = IsPolygonCoplanar(p1,p2);
		if (!coplanar)
			return;
		Array<Plane> edgePlanes1, edgePlanes2;
		ExtractEdgePlanes(p1, edgePlanes1);
		ExtractEdgePlanes(p2, edgePlanes2);
		GetIntersectionVertices(p2, edgePlanes1, result.vertices);
		GetIntersectionVertices(p1, edgePlanes2, result.vertices);
		GetEdgeIntersections(p1,p2,edgePlanes1,edgePlanes2,result.vertices);
		result.plane = p1.plane;
		if (result.vertices.Count()>2)
			SortPolygonVertices(n1, result.vertices);
	}

	void BSP_Compiler::ComputePortals(Scene * scene)
	{
		for (int i=0; i<scene->BSP.Count()-1; i++)
		{
			OnCheckCancel.Invoke(cancelled);
			if (cancelled)
				return;

			if (!scene->BSP[i].IsLeaf())
				continue;
			if (scene->IsNegativeNode(i))
				continue;
			for (int j=i+1; j<scene->BSP.Count(); j++)
			{
				if (!scene->BSP[j].IsLeaf())
					continue;
				if (scene->IsNegativeNode(j))
					continue;
				for (int k=0; k<scene->BSP[i].LeafInfo->BoundingHull.Faces.Count(); k++)
				{
					for (int l=0; l<scene->BSP[j].LeafInfo->BoundingHull.Faces.Count(); l++)
					{
						ConvexHull::Polygon & p1 = *(scene->BSP[i].LeafInfo->BoundingHull.Faces[k]);
						ConvexHull::Polygon & p2 = *(scene->BSP[j].LeafInfo->BoundingHull.Faces[l]);
						if (IsPolygonCoplanar(p1,p2))
						{
							ConvexHull::Polygon * inter = new ConvexHull::Polygon();
							PolygonIntersection(p1,p2, *inter);
							if (inter->vertices.Count()>=3)
							{
								Portal portal;
								portal.Leaf1 = i; portal.Leaf2 = j;
								portal.Polygon = inter;
								scene->Portals.Add(portal);
								scene->BSP[i].LeafInfo->Portals.Add(scene->Portals.Count()-1);
								scene->BSP[j].LeafInfo->Portals.Add(scene->Portals.Count()-1);
							}
							else
								delete inter;
						}
					}
				}
			}
		}
	}

	void BSP_Compiler::MergeBrushes()
	{
		Array<vec3, true> bmin, bmax;
		bmin.SetSize(scene->Brushes.Count());
		bmax.SetSize(scene->Brushes.Count());
		for (int i=0; i<scene->Brushes.Count(); i++)
		{
			bmin[i] = scene->Brushes[i]->vertices[0];
			bmax[i] = scene->Brushes[i]->vertices[0];
			for (int j=1; j<scene->Brushes[i]->vertices.Count(); j++)
			{
				vec3 & v = scene->Brushes[i]->vertices[j];
				for (int k=0; k<3; k++)
				{
					if (v[k] > bmax[i][k]) bmax[i][k] = v[k];
					if (v[k] < bmin[i][k]) bmin[i][k] = v[k];
				}
			}
		}
		Array<Plane> cutPlanes;
		for (int i=0; i<scene->Brushes.Count(); i++)
		{
			OnCheckCancel.Invoke(cancelled);
			if (cancelled)
				return;
			RefPtr<Editor::Brush> obrush = new Editor::Brush(*scene->Brushes[i]);
			for (int j=0; j<scene->Brushes.Count(); j++)
			{
				if (i == j)
					continue;
				if (bmin[i].x > bmax[j].x + 0.1f)
					continue;
				if (bmin[i].y > bmax[j].y + 0.1f)
					continue;
				if (bmin[i].z > bmax[j].z + 0.1f)
					continue;
				if (bmax[i].x < bmin[j].x - 0.1f)
					continue;
				if (bmax[i].y < bmin[j].y - 0.1f)
					continue;
				if (bmax[i].z < bmin[j].z - 0.1f)
					continue;
				
				RefPtr<Editor::Brush> brush = new Editor::Brush(*obrush);
				brush->Polygons.Clear();
				Editor::Brush * bCut = scene->Brushes[j].operator ->();
				for (int k=0; k<obrush->Polygons.Count(); k++)
				{
					bool added = false;
					Editor::Polygon pPoly = obrush->Polygons[k];
					for (int l=0; l<bCut->Polygons.Count(); l++)
					{
						if (bCut->Polygons[l].Normal * obrush->Polygons[k].Normal > -0.99)
							continue;
						vec3 n1 = obrush->Polygons[k].Normal;
						float d1 = -n1*obrush->vertices[pPoly.vertices[0].pos];
						cutPlanes.Clear();
						ConvexHull::Polygon p;
						p.plane.a = bCut->Polygons[l].Normal.x;
						p.plane.b = bCut->Polygons[l].Normal.y;
						p.plane.c = bCut->Polygons[l].Normal.z;
						p.plane.d = -bCut->Polygons[l].Normal*bCut->vertices[bCut->Polygons[l].vertices[0].pos];
						if (fabs(p.plane.d + d1) > 0.1f)
							continue;
						for (int z=0; z<bCut->Polygons[l].vertices.Count(); z++)
							p.vertices.Add(bCut->vertices[bCut->Polygons[l].vertices[z].pos]);
						ExtractEdgePlanes(p, cutPlanes);
						// Cut brush[i].Polygons[k] using cutPlanes
						Editor::Edge eNeg, ePos;
						for (int z=0; z<cutPlanes.Count(); z++)
						{
							if (pPoly.vertices.Count() < 3)
								break;
							Editor::Polygon pNeg, pPos;
							Editor::Polygon::SplitPolygon(cutPlanes[z], brush->vertices, 
								pPoly, brush->vertices, pNeg, brush->vertices, pPos, eNeg, ePos);
							pNeg.Normal = pPoly.Normal;
							pPos.Normal = pPoly.Normal;
							if (pNeg.vertices.Count() >= 3)
							{
								brush->Polygons.Add(pNeg);
								brush->GeneratePolygonTexcoordSystem(brush->Polygons.Count()-1);
								added = true;
							}
							if (pPos.vertices.Count() >= 3)
								added = true;
							pPoly = pPos;
						}
					}
					if (!added && pPoly.vertices.Count() >= 3)
					{
						brush->Polygons.Add(pPoly);
						brush->GeneratePolygonTexcoordSystem(brush->Polygons.Count()-1);
					}
				}
				obrush = brush;
			}
			brushes.Add(obrush);
		}
	}

	void InitializeSurfacePolygons(Scene * scene)
	{
		for (int i=0; i<scene->BSP.Count(); i++)
		{
			if (scene->BSP[i].IsLeaf())
			{
				BSP_Node * node = &scene->BSP[i];
				for (int j=0; j<node->SurfaceCount; j++)
				{
					RefPtr<ConvexHull::Polygon> poly = new ConvexHull::Polygon();
					Surface * surf = &node->Surfaces[j];
					for (int k=0; k<surf->VertexCount; k++)
						poly->vertices.Add(scene->Vertices[surf->Vertices[k]].Position);
					if (poly->vertices.Count()<3)
						continue;
					vec3 norm;
					vec3 v1,v2;
					v1 = poly->vertices[2]-poly->vertices[0];
					v2 = poly->vertices[1]-poly->vertices[0];
					norm = VectorCross(v2,v1);
					Normalize(norm, norm);
					poly->plane.a = norm.x;	poly->plane.b = norm.y;	poly->plane.c = norm.z;
					poly->plane.d = -norm*poly->vertices[0];
					ExtractEdgePlanes(*poly, poly->EdgePlanes);
					node->LeafInfo->SurfacePolygons.Add(poly);
				}
			}
		}
	}

	void FindNeighbours(Scene * scene, int node_id, Array<int, true> & list)
	{
		for (int i=0; i<scene->BSP[node_id].LeafInfo->Portals.Count(); i++)
		{
			Portal & portal = scene->Portals[scene->BSP[node_id].LeafInfo->Portals[i]];
			int neighbour = -1;
			if (portal.Leaf1 == node_id)
				neighbour = portal.Leaf2;
			else
				neighbour = portal.Leaf1;
			list.Combine(neighbour);
		}
	}

	struct PolygonRect
	{
		vec3 axisX, axisY;
		vec3 v0;
		float xmin, xmax, ymin, ymax;
		vec3 GetRandomPoint()
		{
			float x = xmin + (xmax-xmin)*rand()/RAND_MAX;
			float y = ymin + (ymax-ymin)*rand()/RAND_MAX;
			vec3 v;
			v = v0 + axisX*x + axisY*y;
			return v;
		}
	};


	bool PointInPolygon(ConvexHull::Polygon & p, vec3 & point)
	{
		for (int i=0; i<p.EdgePlanes.Count(); i++)
			if (p.EdgePlanes[i].PointDistance(point) < 0.1f)
				return false;
		return true;
	}

	bool BSP_Compiler::TestPortalPortalVisibility(Scene * scene, Portal & p1, Portal & p2)
	{
		if (!p1.Polygon->EdgePlanes.Count())
			ExtractEdgePlanes(*p1.Polygon, p1.Polygon->EdgePlanes);
		if (!p2.Polygon->EdgePlanes.Count())
			ExtractEdgePlanes(*p2.Polygon, p2.Polygon->EdgePlanes);
		PolygonRect r1,r2;
		r1.v0 = p1.Polygon->vertices[0];
		r2.v0 = p2.Polygon->vertices[0];
		EvalPolygonRect(*p1.Polygon, r1.axisX, r1.axisY, r1.xmin, r1.xmax, r1.ymin, r1.ymax);
		EvalPolygonRect(*p2.Polygon, r2.axisX, r2.axisY, r2.xmin, r2.xmax, r2.ymin, r2.ymax);
		float a1,a2;
		a1 = (r1.xmax-r1.xmin)*(r1.ymax-r1.ymin);
		a2 = (r2.xmax-r2.xmin)*(r2.ymax-r2.ymin);
		if (a1<0.5f || a2<0.5f)
			return false;
		int c1 = (int)(a1/(PvsPortalGridSize*PvsSamplingGrid)) + 1;
		int c2 = (int)(a2/(PvsPortalGridSize*PvsSamplingGrid)) + 1;
		if (c1 < MinSamples) c1 = MinSamples;
		if (c2 < MinSamples) c2 = MinSamples;
		for (int i=0; i<c1; i++)
		{
			vec3 v1 = r1.GetRandomPoint();
			int tc = 0;
			while (!PointInPolygon(*p1.Polygon, v1)&& tc<10)
			{
				v1 = r1.GetRandomPoint();
				tc++;
			}
			if (tc!=10)
			{
				for (int j=0; j<c2; j++)
				{
					vec3 v2 = r2.GetRandomPoint();
					tc = 0;
					while (!PointInPolygon(*p2.Polygon, v2) && tc<10)
					{
						v2 = r2.GetRandomPoint();
						tc++;
					}
					if (tc==10)
						return true;
					Intersection inter;
					scene->LineSegIntersection(v1,v2, inter);
					if (!inter.intersected)
						return true;
				}
			}
			else
				return true;
		}
		return false;
	}

	bool BSP_Compiler::TestPortalNodeVisibility(Scene * scene, int node_id, Portal & portal)
	{
		BSP_Node & node = scene->BSP[node_id];
		for (int i=0; i<node.LeafInfo->Portals.Count(); i++)
		{
			Portal & p = scene->Portals[node.LeafInfo->Portals[i]];
			if (TestPortalPortalVisibility(scene, p, portal))
				return true;
		}
		return false;
	}

	void BSP_Compiler::ComputeNodeVisiblity(Scene * scene, int node_id)
	{
		BSP_Node * node = &scene->BSP[node_id];
		RefPtr<Array<int, true>> list1, list2;
		list1 = new Array<int, true>;
		list2 = new Array<int, true>;
		Array<bool, true> processed;
		processed.SetSize(scene->BSP.Count());
		for (int i=0; i<processed.Count(); i++)
			processed[i] = false;
		processed[node_id] = true;
		// Initializing: Add neighbour nodes of node into list1, set them as visible.
		for (int i=0; i<node->LeafInfo->Portals.Count(); i++)
		{
			processed[i] = true;
			bool val = false;
			FindNeighbours(scene, node_id, *list1);
			for (int j=0; j<list1->Count(); j++)
			{
				scene->PVS->Set(node->PVS_ID, scene->BSP[list1->GetItem(j)].PVS_ID, true);
			}
		}
		// Iteration: Add neighbours of nodes in list1 to list2, delete the nodes
		// in list2 that are not visible from node_id, and set the remaining nodes
		// in list2 as visible.
		while (list1->Count())
		{
			list2->Clear();
			for (int i=0; i<list1->Count(); i++)
			{
				int curnode = list1->GetItem(i);
				FindNeighbours(scene, curnode, *list2);
			}
			for (int i=0; i<list2->Count(); i++)
			{
				bool vis = false;
				int cnode = list2->GetItem(i);
				if (processed[cnode])
				{
					list2->Delete(i);
					i--;
					continue;
				}
				if (scene->BSP[cnode].PVS_ID < node->PVS_ID)
					vis = scene->PVS->Get(scene->BSP[cnode].PVS_ID, node->PVS_ID);
				else
				{
					vis = scene->PVS->Get(node_id, list2->GetItem(i));
					if (!vis)
					{
						for (int i=0; i<scene->BSP[cnode].LeafInfo->Portals.Count(); i++)
						{
							int nbr = -1;
							Portal & prt = scene->Portals[scene->BSP[cnode].LeafInfo->Portals[i]];
							if (prt.Leaf1 == cnode)
								nbr = prt.Leaf2;
							else
								nbr = prt.Leaf1;
							if (list1->Exists(nbr))
							{
								vis = TestPortalNodeVisibility(scene, node_id, prt);
								if (vis)
									break;
							}
						}
						
					}
				}
				
				if (!vis)
				{
					list2->Delete(i);
					i--;
				}
				else
					processed[cnode] = true;
			}
			// All nodes in list2 are now ensured to be visible from node
			for (int i=0; i<list2->Count(); i++)
				scene->PVS->Set(scene->BSP[node_id].PVS_ID, scene->BSP[list2->GetItem(i)].PVS_ID, true);
			// Swap lists
			RefPtr<Array<int, true>> pList = list1;
			list1 = list2;
			list2 = pList;
		}
	}

	void BSP_Compiler::ComputeVisibility(Scene * scene)
	{
		Array<int> visleaves;
		int pvsCount = 0;
		for (int i=0; i<scene->BSP.Count(); i++)
		{
			if (scene->BSP[i].IsLeaf() && !scene->IsNegativeNode(i))
			{
				visleaves.Add(i);
				scene->BSP[i].PVS_ID = pvsCount;
				pvsCount ++;
			}
		}
		scene->PVS = new PotentiallyVisibleSet(pvsCount);
		if (!ComputePVS)
		{
			scene->PVS->Clear(0xFF);
			return;
		}
		for (int i=0; i<visleaves.Count(); i++)
		{
			ComputeNodeVisiblity(scene, visleaves[i]);
			int prog = BaseProg + (int)((i/(float)visleaves.Count())*ProgSize);
			OnProgressChanged.Invoke(prog, 100);
			bool cancelled = false;
			OnCheckCancel.Invoke(cancelled);
			if (cancelled)
				return;
		}
	}

	BSP_Compiler::BSP_Compiler(FinalRendering::Editor::Scene *_scene)
	{
		scene = _scene;
		ComputePVS = true;
		cancelled = false;
		PvsSamplingGrid = PvsPortalGridSize;
		MinSamples = 16;
	}

	void CreateReport(Scene * scene, const String & fileName, const String & sceneFileName)
	{
		int poly = 0, visleaves = 0;
		for (int i=0; i<scene->BSP.Count(); i++)
			if (scene->BSP[i].IsLeaf())
			{
				if (!scene->IsNegativeNode(i))
					visleaves ++;
				poly += scene->BSP[i].SurfaceCount;
			}
		int total=0, visible=0;
		total = scene->PVS->GetSize();
		total *= total;
		for (int i=0; i<scene->PVS->GetSize(); i++)
			for (int j=0; j<scene->PVS->GetSize(); j++)
			{
				if (scene->PVS->Get(i,j))
					visible ++;
			}
		float rate = visible/(float)total;

		TextFileStream fs(fileName, gxfmWrite);
		fs.WriteString(L"FinalRendering BSP Build Report\n");
		fs.WriteString(String(L"File Name: \"")+ExtractFileName(sceneFileName)+L"\"\n");
		fs.WriteString(L"------------------------\n");
		fs.WriteString(L"Bounding Box: \n\t[");
		fs.WriteString(String(scene->BBoxMin.x,2) + L", " + String(scene->BBoxMin.y,2) + L", " +
			String(scene->BBoxMin.z,2) + L"]\n\t[" + String(scene->BBoxMax.x,2) + L", " + String(scene->BBoxMax.y,2) + L", " +
			String(scene->BBoxMax.z,2) + L"]");
		fs.WriteString(L"\nVertices: ");
		fs.WriteString(String(scene->Vertices.Count()));
		fs.WriteString(L"\nPolygons: ");
		fs.WriteString(String(poly));
		fs.WriteString(L"\nLightmaps: ");
		fs.WriteString(String(scene->Lightmaps.Count()));
		fs.WriteString(L"\nBSP Nodes: ");
		fs.WriteString(String(scene->BSP.Count()));
		fs.WriteString(L"\nVisleaves: ");
		fs.WriteString(String(visleaves));
		fs.WriteString(L"\nPortals: ");
		fs.WriteString(String(scene->Portals.Count()));
		fs.WriteString(L"\nPVS Efficiency: ");
		fs.WriteString(String((1.0f-rate)*100, 2));
		fs.WriteString(L"%\nMaterials: ");
		fs.WriteString(String(scene->MatNames.Count()));
	}

	void BSP_Compiler::CompilePhysics(const String & fileName)
	{
		NxCookingInterface *cooking = NxGetCookingLib(NX_PHYSICS_SDK_VERSION);
		cooking->NxInitCooking();
		FileStream fs(ExtractFilePath(fileName)+ExtractFileNameWithoutEXT(fileName) +L".phy", gxfmWrite);
		StreamWrapper s(&fs);
		
		PhysSceneHeader h;
		h.StaticBrushCount = scene->Brushes.Count();
		fs.WriteBuffer(&h,1);
		for (int i=0; i<h.StaticBrushCount; i++)
		{
			NxConvexMeshDesc convexDesc;
			convexDesc.numVertices = scene->Brushes[i]->vertices.Count();
			convexDesc.pointStrideBytes = sizeof(vec3);
			convexDesc.points = scene->Brushes[i]->vertices.Buffer();
			convexDesc.flags = NX_CF_COMPUTE_CONVEX;
			cooking->NxCookConvexMesh(convexDesc, s);
		}
	}

	void BSP_Compiler::Compile(const String & fileName)
	{
		CompilePhysics(fileName);

		cancelled = false;
		OnStatusChanged.Invoke(L"Building BSP structure...", L"Merging brushes...");
		OnProgressChanged.Invoke(0,100);
		MergeBrushes();
		OnCheckCancel.Invoke(cancelled);
		if (cancelled)
			return;
		OnProgressChanged.Invoke(2,100);
		OnStatusChanged.Invoke(L"Building BSP structure...", L"Constructing BSP tree...");
		bsp = new FinalRendering::Engine::Scene();
		RefPtr<iScene> interScene = new iScene();
		CompileIntermediateScene(interScene.operator ->());
		OnProgressChanged.Invoke(10,100);
		OnCheckCancel.Invoke(cancelled);
		if (cancelled)
			return;
		OnStatusChanged.Invoke(L"Building BSP structure...", L"Assembling...");
		CompileFinalScene(interScene.operator ->(), bsp.operator ->());
		OnCheckCancel.Invoke(cancelled);
		if (cancelled)
			return;
		OnStatusChanged.Invoke(L"Building BSP structure...", L"Generating portals...");
		ComputePortals(bsp.operator ->());
		OnProgressChanged.Invoke(13,100);
		OnCheckCancel.Invoke(cancelled);
		if (cancelled)
			return;
		OnStatusChanged.Invoke(L"Building BSP structure...", L"Computing visibility...");
		InitializeSurfacePolygons(bsp.operator ->());
		OnProgressChanged.Invoke(15,100);
		OnCheckCancel.Invoke(cancelled);
		if (cancelled)
			return;
		BaseProg = 15; ProgSize = 80;
		ComputeVisibility(bsp.operator ->());
		OnProgressChanged.Invoke(95,100);
		OnCheckCancel.Invoke(cancelled);
		if (cancelled)
			return;
		
		CompileEntity(scene, bsp.operator ->());
		OnStatusChanged.Invoke(L"Building BSP structure...", L"Saving file...");
		bsp->Save(fileName);

		// Clear Entities to avoid free them twice. The IEntity interface is not reference counted.
		bsp->Entities.Clear();

		String reportFile = ExtractFilePath(fileName) + ExtractFileNameWithoutEXT(fileName) + L"_BuildLog.txt";
		CreateReport(bsp.operator ->(), reportFile, fileName);
		OnProgressChanged.Invoke(100,100);
		OnStatusChanged.Invoke(L"Done.", L"");
	}
}